﻿

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
namespace ICodeShare.UI.Controls
{
    /// <summary>
    /// 为ListBox支持数据虚拟化技术
    /// </summary>
    public class VirtualDataForListBox<T> : IDisposable, INotifyPropertyChanged where T : class
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private DelayHelper delay;

        private ListBox listBox;
        /// <summary>
        /// 垂直滚动条
        /// </summary>
        private ScrollBar bar;
        /// <summary>
        /// 滚动视图
        /// </summary>
        private ScrollViewer viewer;
        /// <summary>
        /// 数据源
        /// </summary>
        private ObservableCollection<T> sources;

        /// <summary>
        /// 是否已初始化完毕
        /// </summary>
        protected bool Inited { get; set; }

        /// <summary>
        /// 偏移量
        /// </summary>
        protected double Offset { get; set; }

        /// <summary>
        /// 偏移数量
        /// </summary>
        protected int OffsetCount { get; set; }

        /// <summary>
        /// 偏移方向
        /// <para>True：向上</para>
        /// <para>False：向下</para>
        /// </summary>
        protected bool OffsetDirection { get; set; }

        public Func<bool> CheckCanScrollToBottom;


        #region 数据绑定

        private ObservableCollection<T> virtualData;

        /// <summary>
        /// 虚拟数据
        /// </summary>
        public ObservableCollection<T> VirtualData
        {
            get { return virtualData; }
            protected set
            {
                virtualData = value;
                if (this.PropertyChanged != null)
                {
                    this.PropertyChanged(this, new PropertyChangedEventArgs("VirtualData"));
                }
            }
        }

        #endregion


        #region 配置参数

        /// <summary>
        /// 初始化时最多加载的数据量
        /// <para>需要保证:如果数据未完全加载,ListBox一定可以出现滚动条</para>
        /// </summary>
        [DefaultValue(20)]
        public int InitLoadCount { get; set; }

        /// <summary>
        /// 递增的数量值
        /// <para>滚动条滚动到两端时，每次自动加载的数据量</para>
        /// <para>子项数量超过容器的最大数量<paramref name="MaxCount"/>时，自动减少的数量</para>
        /// </summary>
        [DefaultValue(20)]
        public int IncreasingCount { get; set; }

        /// <summary>
        /// 子项的最大数量
        /// </summary>
        [DefaultValue(60)]
        public int MaxCount { get; set; }

        #endregion



        /// <summary>
        /// 当前显示的虚拟数据起始索引
        /// </summary>
        protected int StartVirtualIndex { get; set; }

        /// <summary>
        /// 当前显示的虚拟数据的终止索引
        /// </summary>
        protected int EndVirtualIndex { get; set; }

        /// <summary>
        /// 忽略滚动条滚动事件
        /// </summary>
        protected bool IgnoreBarChanged { get; set; }



        public VirtualDataForListBox(ListBox listBox, ObservableCollection<T> sources)
        {
            if (listBox == null || sources == null)
                throw new ArgumentException(" listBox or sources is null ");

            this.delay = new DelayHelper(25, DelayLayout);

            this.Inited = false;
            this.Offset = 0;

            this.listBox = listBox;
            this.sources = sources;

            this.InitLoadCount = 20;
            this.IncreasingCount = 20;
            this.MaxCount = 60;

            this.EndVirtualIndex = -1;
            this.StartVirtualIndex = -1;

            this.VirtualData = new ObservableCollection<T>();
        }

        /// <summary>
        /// 初始化
        /// </summary>
        public void Init()
        {
            if (this.Inited)
                return;

            if (this.listBox == null)
            {
                LogHelper.Warning("数据虚拟化-初始化失败");
                return;
            }

            // 监控滚动条
            this.bar = this.listBox.GetFirstChildT<ScrollBar, ListBoxItem>(t => t.Orientation == Orientation.Vertical);
            this.viewer = this.listBox.GetFirstChildT<ScrollViewer, ListBoxItem>(null);

            if (this.bar == null || this.viewer == null)
            {
                LogHelper.Warning("数据虚拟化-初始化失败");
                return;
            }

            // 绑定数据源
            this.listBox.SetBinding(ListBox.ItemsSourceProperty, new Binding("VirtualData") { Source = this, });

            this.ReloadEndData();

            // 监控滚动条
            this.bar.ValueChanged += Bar_ValueChanged;
            // 监控滚动视图
            this.viewer.LayoutUpdated += Viewer_LayoutUpdated;
            // 监控数据源
            this.sources.CollectionChanged += Sources_CollectionChanged;

            Inited = true;
        }

        private void Viewer_LayoutUpdated(object sender, EventArgs e)
        {
            if (!this.Inited)
                return;

            Console.WriteLine(" Viewer_LayoutUpdated ");

            if (this.Offset == 0 || this.IgnoreBarChanged)
                return;

            this.delay.DelayAction();
        }

        private void DelayLayout()
        {
            if (!this.Inited)
                return;

            var view = new ViewDecorate(this.viewer);

            view.DispatcherAction(() =>
            {
                if (this.Offset == 0)
                    return;

                try
                {
                    this.IgnoreBarChanged = true;

                    double temp = 0;
                    // 向上
                    if (this.OffsetDirection)
                    {
                        for (int i = 0; i < this.OffsetCount && i < this.VirtualData.Count; i++)
                        {
                            temp += (this.listBox.ItemContainerGenerator.ContainerFromIndex(i) as ListBoxItem).ActualHeight;
                        }
                    }

                    this.viewer.ScrollToVerticalOffset(this.Offset + temp);
                    Console.WriteLine(" Viewer_LayoutUpdated ----------------------- Over ");
                }
                finally
                {
                    this.Offset = 0;
                    this.IgnoreBarChanged = false;
                }
            });
        }

        /// <summary>
        /// 滚动条滚动
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Bar_ValueChanged(object sender, System.Windows.RoutedPropertyChangedEventArgs<double> e)
        {
            if (!this.Inited)
                return;

            if (this.IgnoreBarChanged || this.Offset != 0)
            {
                e.Handled = true;
                return;
            }

            try
            {
                this.IgnoreBarChanged = true;

                const int count = 100;

                // 向下滚动到端部
                if (e.NewValue > e.OldValue && e.NewValue + count >= this.bar.Maximum)
                {
                    TryScrollDown(e.NewValue - e.OldValue);
                }
                // 向上滚动到端部
                else if (e.NewValue < e.OldValue && e.NewValue - count <= 0)
                {
                    TryScrollUp(e.OldValue - e.NewValue);
                }
            }
            finally
            {
                e.Handled = true;
                this.IgnoreBarChanged = false;
            }
        }

        /// <summary>
        /// 数据源发生变化
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Sources_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (!this.Inited)
                return;

            if (e.Action == NotifyCollectionChangedAction.Add)
            {
                // 新消息到达、尝试将滚动条滚动到底部
                this.MoveToBottom();
            }
            else if (e.Action == NotifyCollectionChangedAction.Remove)
            {
                this.IgnoreBarChanged = true;

                // 移除旧数据
                foreach (var item in e.OldItems)
                {
                    if (item is T)
                        this.VirtualData.Remove(item as T);
                }

                this.ReCalIndex();

                if (this.StartVirtualIndex == -1 || this.EndVirtualIndex == -1)
                {
                    this.ReloadEndData();
                }
                else
                {
                    if (this.VirtualData.Count < this.InitLoadCount)
                    {
                        // 数量过少、尝试填充数据
                        this.LoadMoreData();
                    }
                }

                this.IgnoreBarChanged = false;
            }
            // 撤回消息
            else if (e.Action == NotifyCollectionChangedAction.Replace)
            {
                if (e.OldItems != null && e.OldItems.Count == 1 && e.NewItems != null && e.NewItems.Count == 1)
                {
                    var oldT = e.OldItems[0] as T;
                    var newT = e.NewItems[0] as T;
                    int index = this.VirtualData.IndexOf(oldT);
                    if (index > -1)
                    {
                        this.VirtualData[index] = newT;
                    }
                }

            }
            else if (e.Action == NotifyCollectionChangedAction.Reset)
            {
                this.IgnoreBarChanged = true;

                this.ReloadEndData();

                this.IgnoreBarChanged = false;
            }
        }


        /// <summary>
        /// 将视图移动到某个索引的位置
        /// </summary>
        /// <param name="index"></param>
        public void MoveToIndex(int index)
        {
            if (!this.Inited)
                return;

            if (index < 0 || index >= this.sources.Count)
                return;

            var t = this.sources[index];
            if (this.VirtualData.IndexOf(t) > -1)
            {
                listBox.ScrollIntoView(t);
                return;
            }

            int start = index - this.InitLoadCount;
            if (start < 0)
                start = 0;

            int end = index + this.InitLoadCount;
            if (end >= this.sources.Count)
                end = this.sources.Count - 1;

            int count = end - start + 1;
            if (count == 0)
                return;

            try
            {
                this.IgnoreBarChanged = true;

                var list = this.sources.Skip(start).Take(count);

                this.VirtualData.Clear();

                foreach (var item in list)
                {
                    this.VirtualData.Add(item);
                }

                this.ReCalIndex();

                listBox.ScrollIntoView(t);
            }
            finally
            {
                this.IgnoreBarChanged = false;
            }
        }


        /// <summary>
        /// 将视图移动到底部
        /// </summary>
        public void MoveToBottom()
        {
            if (!this.Inited)
                return;

            try
            {
                this.IgnoreBarChanged = true;

                // 询问是否可以将滚动条滚动到底部
                if (this.CheckCanScrollToBottom != null && !this.CheckCanScrollToBottom())
                    return;

                // 超过最大显示容量、则重新加载末端数据
                if (this.StartVirtualIndex == -1 || this.sources.Count == 0 || this.sources.Count - this.StartVirtualIndex > this.MaxCount)
                {
                    this.ReloadEndData();
                    return;
                }

                // 没有需要加载的数据
                if (this.EndVirtualIndex == this.sources.Count - 1)
                {
                    this.listBox.ScrollViewToBottom();
                    return;
                }

                // 平滑加载
                var count = this.EndVirtualIndex + 1;

                if (this.sources.Count > count)
                {
                    var list = this.sources.Skip(count).ToList();

                    foreach (var item in list)
                    {
                        this.VirtualData.Add(item);
                    }

                    this.ReCalIndex();
                    this.listBox.ScrollViewToBottom();
                }
            }
            catch (Exception ex)
            {
                LogHelper.Execption(ex, "数据虚拟化");
            }
            finally
            {
                this.IgnoreBarChanged = false;
            }
        }


        /// <summary>
        /// 重新计算索引值
        /// </summary>
        private void ReCalIndex()
        {
            if (this.VirtualData.Count > 0)
            {
                this.StartVirtualIndex = this.sources.IndexOf(this.VirtualData[0]);
                this.EndVirtualIndex = this.sources.IndexOf(this.VirtualData[this.VirtualData.Count - 1]);

                if (this.StartVirtualIndex == -1 || this.EndVirtualIndex == -1 || this.EndVirtualIndex < this.StartVirtualIndex)
                {
                    this.StartVirtualIndex = -1;
                    this.EndVirtualIndex = -1;
                    LogHelper.Warning("数据虚拟化-逻辑错误");
                }
            }
            else
            {
                this.StartVirtualIndex = -1;
                this.EndVirtualIndex = -1;
            }
        }


        /// <summary>
        /// 重新初始化数据
        /// </summary>
        private void ReloadEndData()
        {
            if (this.VirtualData.Count > 0)
            {
                this.VirtualData.Clear();

                this.EndVirtualIndex = -1;
                this.StartVirtualIndex = -1;
            }

            if (this.sources != null && this.sources.Count > 0)
            {
                var list = this.sources.ListLastMaxCount(this.InitLoadCount);

                if (list.Count > 0)
                {
                    foreach (var item in list)
                    {
                        this.VirtualData.Add(item);
                    }

                    this.ReCalIndex();

                    // 滚动条滚动到最底部
                    this.listBox.ScrollViewToBottom();
                }
            }
        }


        /// <summary>
        /// 删除数据时加载更多数据
        /// </summary>
        private void LoadMoreData()
        {
            List<T> data = this.sources.ListFindRangeWithMaxCount(this.StartVirtualIndex, this.InitLoadCount);

            if (data.Count <= this.VirtualData.Count)
            {
                // 没有加载到更多数据
                return;
            }

            int start = data.IndexOf(this.VirtualData[0]);
            int end = data.LastIndexOf(this.VirtualData[this.VirtualData.Count - 1]);

            if (start == -1 || end == -1 || end < start)
            {
                LogHelper.Warning("数据虚拟化-逻辑错误");
                return;
            }

            for (int i = 0; i < data.Count; i++)
            {
                if (i < start)
                {
                    this.VirtualData.Insert(i, data[i]);
                }
                else if (i > end)
                {
                    this.VirtualData.Add(data[i]);
                }
            }

            this.ReCalIndex();
        }

        /// <summary>
        /// 向上滚动
        /// </summary>
        private void TryScrollUp(double offset)
        {
            // 没有数据了
            if (this.StartVirtualIndex == -1 || this.StartVirtualIndex == 0)
                return;

            double tempOffset = this.viewer.ContentVerticalOffset;
            // 释放捕获的鼠标
            this.bar.Track.Thumb.ReleaseMouseCapture();
            this.bar.Track.DecreaseRepeatButton.ReleaseMouseCapture();

            int tempCount = 0;

            var list = this.sources.ListLastMaxCount(this.StartVirtualIndex, this.IncreasingCount, false);

            // list 为反序结果
            foreach (var item in list)
            {
                this.VirtualData.Insert(0, item);
                tempCount++;
            }

            if (this.VirtualData.Count > this.MaxCount)
            {
                for (int i = 0; i < this.IncreasingCount; i++)
                {
                    this.VirtualData.RemoveAt(this.VirtualData.Count - 1);
                }
            }

            this.ReCalIndex();

            this.OffsetDirection = true;
            this.OffsetCount = tempCount;
            this.Offset = tempOffset - offset;

            if (this.Offset == 0)
                this.Offset = 1;
        }


        /// <summary>
        /// 向下滚动
        /// </summary>
        private void TryScrollDown(double offest)
        {
            // 没有数据了
            if (this.EndVirtualIndex == -1 || this.EndVirtualIndex == this.sources.Count - 1)
                return;

            // 释放捕获的鼠标
            this.bar.Track.Thumb.ReleaseMouseCapture();
            this.bar.Track.IncreaseRepeatButton.ReleaseMouseCapture();

            double tempOffset = this.viewer.ContentVerticalOffset;

            var list = this.sources.Skip(this.EndVirtualIndex + 1).Take(this.IncreasingCount);

            foreach (var item in list)
            {
                this.VirtualData.Add(item);
            }

            if (this.VirtualData.Count > this.MaxCount)
            {
                for (int i = 0; i < this.IncreasingCount; i++)
                {
                    tempOffset -= (this.listBox.ItemContainerGenerator.ContainerFromIndex(0) as ListBoxItem).ActualHeight;
                    this.VirtualData.RemoveAt(0);
                }
            }

            this.ReCalIndex();

            this.OffsetDirection = false;
            this.OffsetCount = 0;
            this.Offset = tempOffset + offest;

            if (this.Offset == 0)
                this.Offset = 1;
        }


        public void Dispose()
        {
            if (!this.Inited)
                return;

            this.Inited = false;
            this.VirtualData.Clear();

            // 监控滚动条
            this.bar.ValueChanged -= Bar_ValueChanged;
            // 监控滚动视图
            this.viewer.LayoutUpdated -= Viewer_LayoutUpdated;
            // 监控数据源
            this.sources.CollectionChanged -= Sources_CollectionChanged;

            this.CheckCanScrollToBottom = null;

            this.delay.Dispose();
        }
    }
}
